#include <iostream>
#include <stdexcept>
#include <cstring>
using std::cout;
using std::endl;

namespace sympsel {
    class string {
    public:
        friend std::ostream& operator<<(std::ostream& out, const string& s) {
            out.write(s._str, s._size);
            return out;
        }

        string(): _size(0), _capacity(0), _str(new char[1]{'\0'}) {}
        ~string();

        char& operator[](size_t pos) {
            if (pos >= _size) {
                throw std::out_of_range("string index out of range");
            }
            return _str[pos];
        }

        const char& operator[](size_t pos) const {
            if (pos >= _size) {
                throw std::out_of_range("string index out of range");
            }
            return _str[pos];
        }

        explicit string(const char* str = "") : _size(strlen(str)), _capacity(_size + 1), _str(new char[_capacity]) {
            strcpy(_str, str);
        }

        string(const string& str) : _size(str._size), _capacity(str._capacity), _str(new char[_capacity]) {
            strcpy(_str, str._str);
        }

        string& operator=(const string& str) {
            if (this != &str) {
                delete[] _str;
                _size = str._size;
                _capacity = str._capacity;
                _str = new char[_capacity];
                strcpy(_str, str._str);
            }
            return *this;
        }

        void reserve(size_t n);
        void push_back(char c);
        void append(const char* str);
        void clear();
        [[nodiscard]] size_t size() const;
        [[nodiscard]] size_t capacity() const;

        string& operator+=(const char* str) {
            append(str);
            return *this;
        }

        string& operator+=(const string& str) {
            append(str._str);
            return *this;
        }

        string operator+(const string& str) const {
            string new_str = *this;
            new_str.append(str._str);
            return new_str;
        }

        string operator+(const char* str) const {
            string new_str = *this;
            new_str.append(str);
            return new_str;
        }

        int compare(const string& str) const;
        int compare(const char* str) const;
        bool starts_with(const string& prefix) const;
        bool ends_with(const string& suffix) const;
        void replace(size_t pos, size_t len, const char* new_str);
        void replace(size_t pos, size_t len, const string& new_str);

    private:
        size_t _size;
        size_t _capacity;
        char* _str;
    };

    string::~string() {
        delete [] _str;
        _str = nullptr;
        _size = _capacity = 0;
    }

    size_t string::size() const {
        return _size;
    }

    size_t string::capacity() const {
        return _capacity;
    }

    void string::reserve(const size_t n) {
        if (n > _capacity) {
            const auto temp = new char[n];
            strcpy(temp, _str);
            delete [] _str;
            _str = temp;
            _capacity = n;
        }
    }

    void string::push_back(const char c) {
        if (_size == _capacity) {
            reserve(_capacity * 2);
        }
        _str[_size++] = c;
        _str[_size] = '\0';
    }

    void string::append(const char* str) {
        const size_t len = strlen(str);
        if (len + _size > _capacity) {
            size_t new_capacity = _capacity == 0 ? 1 : _capacity;
            while (new_capacity < len + _size) {
                new_capacity *= 2;
            }
            reserve(new_capacity);
        }
        strcpy(_str + _size, str);
        _size += len;
    }

    void string::clear() {
        delete[] _str;
        _str = new char[1]{'\0'};
        _size = 0;
        _capacity = 1;
    }

    int string::compare(const string& str) const {
        return strcmp(_str, str._str);
    }

    int string::compare(const char* str) const {
        return strcmp(_str, str);
    }

    bool string::starts_with(const string& prefix) const {
        return strncmp(_str, prefix._str, prefix._size) == 0;
    }

    bool string::ends_with(const string& suffix) const {
        if (suffix._size > _size) return false;
        return strncmp(_str + _size - suffix._size, suffix._str, suffix._size) == 0;
    }

    void string::replace(size_t pos, size_t len, const char* new_str) {
        if (pos >= _size) {
            throw std::out_of_range("string index out of range");
        }
        size_t new_len = strlen(new_str);
        if (len > _size - pos) len = _size - pos;
        if (_size + new_len - len > _capacity) {
            size_t new_capacity = _capacity;
            while (new_capacity < _size + new_len - len) {
                new_capacity *= 2;
            }
            reserve(new_capacity);
        }
        memmove(_str + pos + new_len, _str + pos + len, _size - pos - len);
        memcpy(_str + pos, new_str, new_len);
        _size += new_len - len;
        _str[_size] = '\0';
    }

    void string::replace(size_t pos, size_t len, const string& new_str) {
        replace(pos, len, new_str._str);
    }
}

